using System.Collections.Concurrent;

namespace Gj.MapData
{
    public class ScheduleBuilder
    {
        public static ConcurrentQueue<ScheduleBuilder> Queue = new();
        private int _alloc;
        internal int _delay;

        internal StandardExecutor _executor;

        internal DefineData _from;
        internal string _key;
        internal ScheduleLogic _logic;
        internal string _mapNoId;
        internal object[] _param;
        internal SubmitData _schedule;
        internal DefineData _target;
        internal long _time;

        public static ScheduleBuilder Get()
        {
            ScheduleBuilder builder = null;
            if (Queue.TryDequeue(out builder))
            {
            }

            if (builder == null) builder = new ScheduleBuilder();
            return builder;
        }

        public static ScheduleBuilder Get(StandardExecutor executor)
        {
            var builder = Get();
            builder.BindExecutor(executor);

            return builder;
        }

        internal static void Release(ScheduleBuilder builder)
        {
            if (builder._alloc == 0)
            {
                builder._from = null;
                builder._executor = null;
                builder._alloc = 0;
                builder._param = null;
                builder._schedule = null;
                Queue.Enqueue(builder);
            }
            else
            {
                builder._alloc--;
            }
        }

        public ScheduleBuilder Alloc()
        {
            _alloc++;
            return this;
        }

        public EventBuilder ToEvent()
        {
            var eventBuilder = EventBuilder.Get(_executor);

            eventBuilder.Schedule(this);
            return eventBuilder;
        }

        public ScheduleBuilder BindExecutor(StandardExecutor executor)
        {
            _executor = executor;
            _mapNoId = executor.CurrentMapNoId;
            return this;
        }

        public ScheduleBuilder BindMapNoId(string mapNoId)
        {
            _mapNoId = mapNoId;
            return this;
        }

        public ScheduleBuilder Bind(DataType dataType, object id, string key, long time = 0, object[] param = null,
            int delay = 0)
        {
            _target = new BaseData { DataType = dataType, Id = id };
            _time = time;
            _key = key;
            _param = param ?? new object[] { };
            _delay = delay;
            return this;
        }

        public ScheduleBuilder Bind(DefineData data, string key, long time = 0, object[] param = null, int delay = 0)
        {
            _target = data;
            _time = time;
            _key = key;
            _param = param ?? new object[] { };
            _delay = delay;
            return this;
        }

        internal SubmitData Data()
        {
            var tmp = _schedule;
            Release(this);
            return tmp;
        }

        internal ScheduleContent Content()
        {
            var content = new ScheduleContent();
            content.Content = _schedule;
            content.From = _from;
            content.Target = _target;
            content.Schedule = _logic;
            content.Key = _key;
            content.Time = _time;
            content.Delay = _delay;
            Release(this);
            return content;
        }

        public ScheduleBuilder From(DefineData from)
        {
            _from = from;
            return this;
        }

        public void Schedule()
        {
            _executor.Schedule(this);
            Release(this);
        }

        public ScheduleBuilder Once(EventBuilder builder)
        {
            var schedule = new OnceScheduleContent();
            schedule.Event = builder.Data();
            _schedule = schedule;
            _logic = ScheduleLogic.Once;
            return this;
        }

        public ScheduleBuilder Repeat(long repeat, EventBuilder builder, long endDelay = 0)
        {
            var schedule = new RepeatScheduleContent();
            schedule.Repeat = repeat;
            schedule.EndTime = endDelay;
            schedule.Event = builder.Data();

            _schedule = schedule;
            _logic = ScheduleLogic.Repeat;
            return this;
        }

        public ScheduleBuilder Repeat(long repeat, DataType dataType, OperateBuilder builder,
            CheckBuilder checkBuilder = null, long endDelay = 0)
        {
            var schedule = new RepeatEveryScheduleContent();
            schedule.Checks = checkBuilder.Data();
            schedule.Repeat = repeat;
            schedule.EndTime = endDelay;
            schedule.DataType = dataType;
            schedule.Operates = builder.DataOfUpdate();

            _schedule = schedule;
            _logic = ScheduleLogic.RepeatEvery;
            return this;
        }

        public ScheduleBuilder Move(MoveScheduleContent scheduleContent)
        {
            _schedule = scheduleContent;
            _logic = ScheduleLogic.Move;
            return this;
        }

        public ScheduleBuilder Touch(TouchScheduleContent touchScheduleContent)
        {
            _schedule = touchScheduleContent;
            _logic = ScheduleLogic.Touch;
            return this;
        }

        public ScheduleBuilder Query(QueryScheduleContent queryScheduleContent)
        {
            _schedule = queryScheduleContent;
            _logic = ScheduleLogic.Query;
            return this;
        }
    }
}